using System.Collections.Generic;

namespace Gj.Services
{
    public enum AttrTarget : byte
    {
        None,
        First,
        Second,
        FirstMaster,
        SecondMaster
    }

    public enum AttrComputeTarget : byte
    {
        First = 0,
        Second = 1,
        Logic = 2,
        Runtime = 3,
        Var = 4,
        Value = 5,
        Attr = 6,
        Prop = 7,
        Buff = 8,
        FirstMaster = 9,
        SecondMaster = 10
    }

    public class AttrService : AttrManager, GameService, AttrDataManager, AttrComputeInterface
    {
        public static AttrService single;

        public static AttrTools tools;

        private readonly List<ushort> _processData = new();

        private readonly List<ushort> _realtimeIndex = new();
        private List<ushort> processData;
        private ushort[] realtimeIndex;

        static AttrService()
        {
            single = new AttrService();
            tools = new AttrTools(single, single);
        }

        public int ParseComputeTarget(AttrCompute compute, int[] vars, int[] logicParam, int[] runtimeParam,
            AttrEnv attrEnv)
        {
            switch ((AttrComputeTarget)compute.target)
            {
                case AttrComputeTarget.First:
                    return attrEnv.first.GetAttr().GetWithId(compute.value);
                case AttrComputeTarget.FirstMaster:
                    var fm = ((Control)attrEnv.first).GetMaster();
                    if (fm == null) return 0;
                    return fm.GetAttr().GetWithId(compute.value);
                case AttrComputeTarget.Second:
                    return attrEnv.second.GetAttr().GetWithId(compute.value);
                case AttrComputeTarget.SecondMaster:
                    var sm = ((Control)attrEnv.second).GetMaster();
                    if (sm == null) return 0;
                    return sm.GetAttr().GetWithId(compute.value);
                case AttrComputeTarget.Logic:
                    return logicParam[compute.value];
                case AttrComputeTarget.Runtime:
                    return runtimeParam[compute.value];
                case AttrComputeTarget.Var:
                    return vars[compute.value];
                case AttrComputeTarget.Value:
                case AttrComputeTarget.Attr:
                case AttrComputeTarget.Prop:
                case AttrComputeTarget.Buff:
                    return compute.value;
                default:
                    return 0;
            }
        }

        public void OnUpdate()
        {
            
        }

        public void OnDestroy()
        {
            single = new AttrService();
            tools = new AttrTools(single, single);
        }

        public void OnInit()
        {
        }

        public void AddRealtimeIndex(ushort index)
        {
            _realtimeIndex.Add(index);
        }

        public void AddProcessData(ushort index)
        {
            _processData.Add(index);
        }

        public ushort[] GetProcessData()
        {
            return processData.ToArray();
        }

        public void End()
        {
            processData = new List<ushort>();
            processData.AddRange(_processData);

            realtimeIndex = new ushort[_realtimeIndex.Count];
            for (var i = 0; i < _realtimeIndex.Count; i++) realtimeIndex[i] = _realtimeIndex[i];
            _realtimeIndex.Clear();
            Start();
        }

        public ushort[] GetRealtime()
        {
            return realtimeIndex;
        }

        public bool ContainsProcessData(ushort index)
        {
            return processData != null && processData.Contains(index);
        }

        public static Life GetLife(Control first, Control second, AttrTarget o)
        {
            switch (o)
            {
                case AttrTarget.First:
                    return first as Life;
                case AttrTarget.Second:
                    return second as Life;
                case AttrTarget.FirstMaster:
                    return first?.GetMaster() as Life;
                case AttrTarget.SecondMaster:
                    return second?.GetMaster() as Life;
                default:
                    return null;
            }
        }
    }

    /**
     * 转换技能
     */
    public class TransformProcess : ActionProcess
    {
        public bool PreEnv(AttrEventInfo info, int[] vars, AttrEnv attrEnv)
        {
            if (attrEnv.second != null) return false;
            if (vars.Length < 2) return false;
            if (attrEnv.result is Skill)
            {
                vars[1] = 0;
                return true;
            }

            return false;
        }

        public bool PreLogic(AttrEventInfo info, AttrLogic logic, int[] vars, AttrEnv attrEnv)
        {
            // 已经获取到技能，根据优先级，结束后续判断
            if (vars[1] > 0) return false;
            vars[0] = 0;
            return true;
        }

        public bool Process(AttrEventInfo info, AttrLogic logic, int[] vars, AttrEnv attrEnv)
        {
            var skillId = 0;
            if (vars[1] > 0) return false;
            if (vars[0] == 0)
            {
                skillId = vars[0];
                vars[1] = vars[0];
            }

            if (skillId > 0) attrEnv.result = EngineService.single.GetSkill(skillId);

            return false;
        }
    }

    /**
     * 触发技能
     */
    public class TriggerProcess : ActionProcess
    {
        public bool PreEnv(AttrEventInfo info, int[] vars, AttrEnv attrEnv)
        {
            if (attrEnv.first == null || attrEnv.second == null) return false;
            if (vars.Length < 1) return false;
            return true;
        }

        public bool PreLogic(AttrEventInfo info, AttrLogic logic, int[] vars, AttrEnv attrEnv)
        {
            // 已经获取到技能，根据优先级，结束后续判断
            if (vars[1] > 0) return false;
            vars[0] = 0;
            return true;
        }

        public bool Process(AttrEventInfo info, AttrLogic logic, int[] vars, AttrEnv attrEnv)
        {
            var skillId = 0;
            if (vars[0] > 0) skillId = vars[0];
            if (skillId > 0)
            {
                var skill = EngineService.single.GetSkill(skillId);
                if (skill == null) return false;
                var life = AttrService.GetLife((Control)attrEnv.first, (Control)attrEnv.second,
                    (AttrTarget)logic.param[0]);
                if (life == null) return false;
                var castPart = ((Control)attrEnv.first).GetGameObject().GetComponent<CastPart>();
                if (attrEnv.first == life)
                    castPart.Cast(skill);
                else
                    castPart.Cast(skill, life);
            }

            return false;
        }
    }

    // 触发change
    public class CastChangeProcess : ActionProcess
    {
        public bool PreEnv(AttrEventInfo info, int[] vars, AttrEnv attrEnv)
        {
            if (vars.Length < 2) return false;
            if (attrEnv.result is bool result)
            {
                if (result) attrEnv.co = Change.Make();
                return result;
            }

            vars[0] = 0;
            vars[1] = -1;
            return false;
        }

        public bool PreLogic(AttrEventInfo info, AttrLogic logic, int[] vars, AttrEnv attrEnv)
        {
            if (attrEnv.result is bool result && !result) return false;
            return true;
        }

        public bool Process(AttrEventInfo info, AttrLogic logic, int[] vars, AttrEnv attrEnv)
        {
            if (vars[0] == 0 || vars[1] < 0) return false;
            var change = (Change)attrEnv.co;
            if (logic.param[2] > 0) change.specialList.Add(logic.param[2]);
            if (logic.param[1] == 0 || logic.param[0] == 0) return false;
            var life = AttrService.GetLife((Control)attrEnv.first, (Control)attrEnv.second, (AttrTarget)logic.param[0]);
            if (life == null) return false;

            change.Bind((ChangeType)logic.param[1], (int)(vars[0] * (attrEnv.power + 1)), vars[1]);

            if (info.type > 0)
            {
                var cast = life as Cast;
                if (cast == null) return false;
                attrEnv.result = cast.OnCheckChange(change);
            }
            else
            {
                life.OnChange((Control)attrEnv.first, change);
            }

            return false;
        }
    }
}